package com.gph.saviorframework.fastdfs.service.impl;

import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.io.IOUtils;

import com.gph.saviorframework.fastdfs.FileInfo;
import com.gph.saviorframework.fastdfs.NameValuePair;
import com.gph.saviorframework.fastdfs.StorageClient;
import com.gph.saviorframework.fastdfs.StorePath;
import com.gph.saviorframework.fastdfs.exception.FdfsIOException;
import com.gph.saviorframework.fastdfs.proto.OtherConstants;
import com.gph.saviorframework.fastdfs.proto.handler.ICmdProtoHandler;
import com.gph.saviorframework.fastdfs.proto.handler.StorageAppendHandler;
import com.gph.saviorframework.fastdfs.proto.handler.StorageDeleteHandler;
import com.gph.saviorframework.fastdfs.proto.handler.StorageDownloadHandler;
import com.gph.saviorframework.fastdfs.proto.handler.StorageGetMetadataHandler;
import com.gph.saviorframework.fastdfs.proto.handler.StorageModifyHandler;
import com.gph.saviorframework.fastdfs.proto.handler.StorageQueryFileInfoHandler;
import com.gph.saviorframework.fastdfs.proto.handler.StorageSetMetadataHandler;
import com.gph.saviorframework.fastdfs.proto.handler.StorageTruncateHandler;
import com.gph.saviorframework.fastdfs.proto.handler.StorageUploadHandler;
import com.gph.saviorframework.fastdfs.proto.handler.StorageUploadSlaveHandler;
import com.gph.saviorframework.fastdfs.service.IFdfsFileInputStreamHandler;
import com.gph.saviorframework.fastdfs.service.IStorageClientService;
import com.gph.saviorframework.fastdfs.service.ITrackerClientService;
import com.gph.saviorframework.fastdfs.socket.FdfsInputStream;
import com.gph.saviorframework.fastdfs.socket.FdfsSocket;
import com.gph.saviorframework.fastdfs.socket.FdfsSocketService;
import com.gph.saviorframework.fastdfs.socket.PooledFdfsSocket;

public class StorageClientService implements IStorageClientService {

	private FdfsSocketService fdfsSocketService;

	private ITrackerClientService trackerClientService;

	private <T> T process(FdfsSocket socket, ICmdProtoHandler<T> handler) {
		try {
			return handler.handle();
		} finally {
			IOUtils.closeQuietly(socket);
		}
	}

	public StorePath uploadFile(String groupName, InputStream ins, long size,
			String ext) {
		StorageClient storageClient = trackerClientService
				.getStoreStorage(groupName);

		FdfsSocket socket = fdfsSocketService.getSocket(storageClient
				.getInetSocketAddress());
		ICmdProtoHandler<StorePath> handler = new StorageUploadHandler(socket,
				false, ins, size, storageClient.getStoreIndex(), ext,
				storageClient.getCharset());
		return process(socket, handler);
	}

	public StorePath uploadAppenderFile(String groupName, InputStream ins,
			long size, String ext) {
		StorageClient storageClient = trackerClientService
				.getStoreStorage(groupName);
		FdfsSocket socket = fdfsSocketService.getSocket(storageClient
				.getInetSocketAddress());
		ICmdProtoHandler<StorePath> handler = new StorageUploadHandler(socket,
				true, ins, size, storageClient.getStoreIndex(), ext,
				storageClient.getCharset());
		return process(socket, handler);
	}

	public StorePath uploadSlaveFile(String groupName, String masterFilename,
			InputStream ins, long size, String prefixName, String ext) {
		StorageClient storageClient = trackerClientService.getUpdateStorage(
				groupName, masterFilename);
		FdfsSocket socket = fdfsSocketService.getSocket(storageClient
				.getInetSocketAddress());
		ICmdProtoHandler<StorePath> handler = new StorageUploadSlaveHandler(
				socket, ins, size, masterFilename, prefixName, ext,
				storageClient.getCharset());
		return process(socket, handler);
	}

	public void appendFile(String groupName, String path, InputStream ins,
			long size) {
		StorageClient storageClient = trackerClientService.getUpdateStorage(
				groupName, path);
		FdfsSocket socket = fdfsSocketService.getSocket(storageClient
				.getInetSocketAddress());
		ICmdProtoHandler<Void> handler = new StorageAppendHandler(socket, ins,
				size, path, storageClient.getCharset());
		process(socket, handler);
	}

	public void modifyFile(String groupName, String path, long offset,
			InputStream ins, long size) {
		StorageClient storageClient = trackerClientService.getUpdateStorage(
				groupName, path);
		FdfsSocket socket = fdfsSocketService.getSocket(storageClient
				.getInetSocketAddress());
		ICmdProtoHandler<Void> handler = new StorageModifyHandler(socket, ins,
				size, path, offset, storageClient.getCharset());
		process(socket, handler);
	}

	public void deleteFile(String groupName, String path) {
		StorageClient storageClient = trackerClientService.getUpdateStorage(
				groupName, path);

		FdfsSocket socket = fdfsSocketService.getSocket(storageClient
				.getInetSocketAddress());
		ICmdProtoHandler<Void> handler = new StorageDeleteHandler(socket,
				groupName, path, storageClient.getCharset());
		process(socket, handler);
	}

	public void truncateFile(String groupName, String path,
			long truncatedFileSize) {
		StorageClient storageClient = trackerClientService.getUpdateStorage(
				groupName, path);

		FdfsSocket socket = fdfsSocketService.getSocket(storageClient
				.getInetSocketAddress());
		ICmdProtoHandler<Void> handler = new StorageTruncateHandler(socket,
				path, truncatedFileSize, storageClient.getCharset());
		process(socket, handler);

	}

	public <T> T downloadFile(String groupName, String path,
			IFdfsFileInputStreamHandler<T> handling) {
		long offset = 0;
		long size = 0;

		return downloadFile(groupName, path, offset, size, handling);
	}

	public <T> T downloadFile(String groupName, String path, long offset,
			long size, IFdfsFileInputStreamHandler<T> handling) {
		StorageClient storageClient = trackerClientService.getFetchStorage(
				groupName, path);

		FdfsSocket socket = fdfsSocketService.getSocket(storageClient
				.getInetSocketAddress());
		ICmdProtoHandler<FdfsInputStream> handler = new StorageDownloadHandler(
				socket, groupName, path, offset, size,
				storageClient.getCharset());

		try {
			FdfsInputStream fdfsInputStream = handler.handle();
			T result = handling.deal(fdfsInputStream);

			if (!fdfsInputStream.isReadCompleted()
					&& socket instanceof PooledFdfsSocket) {
				((PooledFdfsSocket) socket).setNeedDestroy(true);
			}
			IOUtils.closeQuietly(fdfsInputStream);
			return result;
		} catch (IOException e) {
			if (socket instanceof PooledFdfsSocket) {
				((PooledFdfsSocket) socket).setNeedDestroy(true);
			}
			throw new FdfsIOException(e);
		} finally {
			IOUtils.closeQuietly(socket);
		}

	}

	public NameValuePair[] getMetadata(String groupName, String path) {
		StorageClient storageClient = trackerClientService.getFetchStorage(
				groupName, path);
		FdfsSocket socket = fdfsSocketService.getSocket(storageClient
				.getInetSocketAddress());
		ICmdProtoHandler<NameValuePair[]> handler = new StorageGetMetadataHandler(
				socket, groupName, path, storageClient.getCharset());
		return process(socket, handler);
	}

	public void overwriteMetadata(String groupName, String path,
			NameValuePair[] metaList) {
		StorageClient storageClient = trackerClientService.getUpdateStorage(
				groupName, path);
		FdfsSocket socket = fdfsSocketService.getSocket(storageClient
				.getInetSocketAddress());
		ICmdProtoHandler<Void> handler = new StorageSetMetadataHandler(socket,
				groupName, path, metaList,
				OtherConstants.STORAGE_SET_METADATA_FLAG_OVERWRITE,
				storageClient.getCharset());
		process(socket, handler);

	}

	public void mergeMetadata(String groupName, String path,
			NameValuePair[] metaList) {
		StorageClient storageClient = trackerClientService.getUpdateStorage(
				groupName, path);
		FdfsSocket socket = fdfsSocketService.getSocket(storageClient
				.getInetSocketAddress());
		ICmdProtoHandler<Void> handler = new StorageSetMetadataHandler(socket,
				groupName, path, metaList,
				OtherConstants.STORAGE_SET_METADATA_FLAG_MERGE,
				storageClient.getCharset());
		process(socket, handler);

	}

	public FileInfo queryFileInfo(String groupName, String path) {
		StorageClient storageClient = trackerClientService.getFetchStorage(
				groupName, path);
		FdfsSocket socket = fdfsSocketService.getSocket(storageClient
				.getInetSocketAddress());
		ICmdProtoHandler<FileInfo> handler = new StorageQueryFileInfoHandler(
				socket, groupName, path, storageClient.getCharset());
		return process(socket, handler);
	}

	/**
	 * @param trackerClientService
	 */
	public void setTrackerClientService(
			ITrackerClientService trackerClientService) {
		this.trackerClientService = trackerClientService;
	}

	/**
	 * @param fdfsSocketService
	 *            the fdfsSocketService to set
	 */
	public void setFdfsSocketService(FdfsSocketService fdfsSocketService) {
		this.fdfsSocketService = fdfsSocketService;
	}

}
